perm filename POINTY.DOC[PNT,HE]7 blob
sn#409016 filedate 1979-01-05 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00015 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 PURPOSE OF THIS FILE
C00003 00003 IMPLEMENTATION COMMENTS
C00007 00004 THE SCANNER
C00014 00005 THE PARSER
C00021 00006 SYMBOL TABLE ORGANIZATION
C00030 00007 INITIALIZATIONS
C00034 00008 FILE TABLE ORGANIZATION
C00039 00009 DISPLAY
C00044 00010 HOW "KILL" IS IMPLEMENTED
C00049 00011 DEFAULT PARTS
C00052 00012 HOW "CONSTRUCT" IS IMPLEMENTED
C00055 00013 HOW TO HANDLE THE ROTATION MATRIX
C00063 00014 THE EXPRESSION EVALUATOR
C00067 00015 SOURCE FILE PROTOCOL
C00070 ENDMK
C⊗;
PURPOSE OF THIS FILE
This file is a legacy of Maria Gini and Pina Gini left behind
to explain in an informal way the control structure of the final POINTY
system, and meant to help anybody who plans to modify/use POINTY.
The file was originally put together by Maria (whose programmer name is MLG)
whom we should verbally thank each time we use this file instead of
having to chase through half a dozen files trying to figure where is
what.
Shahid 1-5-78. [MSM]
This file is updated to reflect the current state of implementation
as far as possible.
IMPLEMENTATION COMMENTS
Some comments about some implementation choices are in next pages.
The program is in the file POINTY.SAI[PNT,HE].
It requires some source files and load modules
conditional source file load module
flag
MAINPR.SAI[PNT,HE]
MACROS.SAI[PNT,HE]
RECORD.DEF[PNT,HE]
PARSER.HDR[PNT,HE] PARSER[PNT,HE]
OPERAT.HDR[PNT,HE] OPERAT[PNT,HE]
OUTPUT.HDR[PNT,HE] OUTPUT[PNT,HE]
ARMINT.SAI[PNT,MSM] TLKF5a[PNT,HE]
#KILL KILLER.HDR[PNT,HE] KILLER[PNT,HE]
#HELP HELP.HDR[PNT,HE] HELP[PNT,HE]
#ARROW ARROW[PNT,HE]
#DISPL DISPLY[PNT,HE]
#OUTPT INPOUT[PNT,HE]
#MOVE ( TLKF3a[PNT,HE]
( MOVARM[PNT,HE]
( BEJCZY[PNT,HE]
( ARMSOL[PNT,HE]
file purpose
HELP used by HELP.SAI to read the explanations about the syntax.
POINTY.SAI outermost file with specified compilation flags for a complete
version of POINTY.
MACROS.SAI useful macro definitions
RECORD.DEF external declarations for the record classes, the
symbol table and the pointers to the predefined variables
PARSER.SAI basic parsing procedures
OPERAT.SAI basic arithmetic operations
EXPR.SAI procedures for parsing and calling the arithmetic expressions
OUTPUT.SAI procedures to construct the string with the decoded
values of the different variables for printing
DISPLY.SAI display procedures
INPOUT.SAI for file output (read is in MAINPR)
KILLER.SAI procedures required by KILL instruction
HELP.SAI procedures for the complete error explanations and syntax
messages
ARROW.SAI contains procedure for drawing arrow on the display.
**********************
The previous working version of POINTY is POINTY.OLD[PNT,HE].
The dump file is OLD.DMP[PNT,HE]. This was the state of the world
as of end of December, 1977, and meant as backup in case there
are some bugs in the new version.
THE SCANNER
The current line is saved in $CLNE. The part of line remaining to be scanned
is in $CLINR. Initially $CLINR=$CLNE. $CLNE may at any time contain exactly,
or more or less than one instruction. The actual process of reading getting
the next line is done in procedure NEWLINE, which remembers which is the
current input device - the tetetype, disk or a tempory buffer from the
teletype which is used to type correction information and responses to prompts.
GTOKEN obtains the next token and returns certain information in the global
variables TOKEN, TOKENINDEX, TOKENPTR, RES_CLASS depending on the nature of
the token.
Anything within curly brackets (and the brackets themselves) is totally
ignored by GTOKEN, so that comments may be enclosed within curly brackets.
Since this is handled by GTOKEN, the parser never sees curly brackets.
Different break tables are used by the parser and the scanner.
The general table used in the main loop to separate one instruction from
the others is $SCNTAB. Its specifications are expressed in SAIL by
SETBREAK ($SCNTAB←GETBREAK,";?{",CR&LF&FF&TV,"INAK");
THE SCANNER
The procedure GTOKEN reads the next token in the string $CLNR and
returns it in TOKEN. The syntactic type of TOKEN is in #TOKEN.
It is ID_TYPE for identifiers,
INT_TYPE for integer numbers,
REAL_TYPE for real numbers,
OPERATOR_TYPE for punctuation marks, operators..
UNDECLARED_TYPE for undeclared variables,
RESERVED_TYPE for reserved words.
GTOKEN uses different break tables
BTABLE←".,;[]()+-*/←↑↓→?α$"&LF&CR&TAB&FF&SP;
SETBREAK ($RETAB ←GETBREAK,BTABLE,NULL,"INR");
is used to read the token, retaining the break character
SETBREAK ($SKTAB ←GETBREAK,BTABLE,NULL,"INS");
is used to read the token after using $RETAB, just to skip the break
character left before in the string
SETBREAK ($SPCTAB←GETBREAK,TAB&SP,NULL, "XNR");
is used to skip the blanks before the token
SETBREAK ($ALFTAB←GETBREAK,NULL,NULL,"XRN");
is used to read one character from the string, usually to check if it's a
number or not
SETBREAK ($NUMTAB←GETBREAK,"@+-0123456789",NULL,"XNR"); ! as table 10;
is used while reading a number to detect the first non-number character
GTOKEN is to skips blanks and tabs, and reads the token
retaining the break character. The action is then dictated by the token and
the break. If the token is null there are two cases: can be a number (as
.12@3) or simply a punctuation mark(as . or ,) or the end of the string in
one instruction requiring more typing(instruction typed in different
lines). So these cases are identified taking a look at the break
character and deciding either to wait for more input (break=<cr> and flag
indicating the instruction is not terminated), or to return a punctuation
mark or again to continue reading a number. If the break character is a .
two cases are possible, because it can be part of a floating number or can
be only a punctuation mark.
If the token read at the beginning is not null two different cases are
possible: the break is a . or is a different character so the token is an
identifier.
In the first case (break .) the string is scanned to see if the token is
composed only by numbers (in that case is a floating number and other more
numbers can be present after .) or if the token is simply an identifier
followed by a .
If after this process the token results to be an identifier there is
another check to be done, because it can be an integer number. So the
token is scanned again, looking at the first character to see if it's a
number. In that case the other characters are checked to define the
correct type of the token.
Other break tables are used for different purposes.
* $DSTTAB is used by COPY/MERGE instructions to find if there is an
underscore in the name of the frames.
* $ERRTAB is used by the recover procedure to check if the new name typed
is an identifier. If a break character is found the recover will proceed
asking to type again a correct isdentifier. During the scanning spaces and
<cr>'s are eliminated.
* $BSKTAB is used for the display to compact the output, by erasing the
spaces.
THE PARSER
Parsing is done by the procedure PARSE.
PARSE checks the first token of the instruction to
detect what kind of instruction it is. In fact the instructions begin
with a reserved word, unless they are assignment instructions (or the
reserved word is mispelled!!), and calls the relevant procedure to
parse the rest of the statement.
SYMBOL TABLE ORGANIZATION
The symbol table is constituted by an array $YMTAB, whose elements are
rptr's to records of class SYMBOL.
* the number of positions in $YMTAB is #LMT+1, with #LMT=499.
The symbol table is then divided in parts, one for each type of variables.
* the number of the types is #NTYPE, with #NTYPE=#MAX-#MIN+1
where #MIN = 1 and #MAX = 5.
They correspond to SCALAR, VECTOR, ROT, FRAME, TRANS, MACRO, FUNCTION
* the number of positions for each type is #LTYPE. It's given by
#LTYPE= 70.
* the type SCALAR is in the first part of $YMTAB (#SC=1=#MIN), the
type VECTOR in the second (#VT=2), the type ROT in the third (#RT=3),
the type TRANS in the fourth (#TR=4) and the type FRAME in the fifth
(#FR=5).
* #SC, #VT, #RT, #FR, #TR, #MC and #FN are used to enter in the
appropriate part of $YMTAB.
The records of class SYMBOL have several fields
pname, is the name of the symbol (string),
object, is the rptr to the record of the appropriate class.
nuses, the number of different outside variables used by this symbol
currently appropriate for functions
nusedby, the number of different functions that use this symbol
valid, a boolean flag telling whether this is a valid symbol in
in the symbol table or deleted from the symbol table
arrays usedby[1:nusedby] which is an array of the expression record
pointers to symbols which use this symbol
array uses[1:nuses} which is an array of expression record pointers
to symbols which are used by this symbol.
The integer array $ENTRY is used to keep track of the number of elements
inserted in $YMTAB.
* the number of positions in $ENTRY is #NTYPE
* each position in $ENTRY corresponds to one type of variables
* each position contains the entry in $YMTAB of the first position
free for the corresponding type.
* the range of values for the i-element of $ENTRY (orresponding to all
the entries in $YMTAB for the class i) is given by
#LTYPE*(i-#MIN)≤ $ENTRY[i] ≤ (#LTYPE*i)-#MIN
* when entries from the symbol table are deleted, $ENTRY value is
reduced by 1, and the last symbol of this type in the symbol table
is shifted up to occupy the vacant spot in $YMTAB. This means that
space can be created in the symbol table by deleting some previously
declared symbols.
The different types of variables are stored on different types of records.
* SCALAR
scalar:value contains the real value of the scalar
* VECTOR
vector:xc contains the real value of the component along x-axis
vector:yc contains the real value of the component along y-axis
vector:zc contains the real value of the component along z-axis
* ROT
rot:xf is a real array[1:5,1:4], as in FRAME
* FRAME
frame:pname contains the pname of the frame (string)
frame:dad contains the pointer to its dad in the tree
frame:son contains the pointer to its last son
frame:ebro contains the pointer to its elder brother
frame:ybro contains the pointer to its younger brother
frame:howlinked contains the kind of affixment(#RGDLK,#NRGLK,#INDLK)
frame:xf is a real arry containing
xf[1:3,1:3]=rotation matrix,
xf[1:3,4]=translation vector,
xf[4,1:3]=0,
xf[4,4]=1,
xf[5,1:3]=rotation angles (euler angles)
xf[5,4]>0 if angles are valid;
* TRANS
trans:xf is a real array [1:5,1:4], as in FRAME
SYMTAB[0:lmt] symbol scalar
______ _______________ _________
| | | | | | |
| -|---------→|pname|object-|--------→| value |
|____| |_____|_______| |_______|
| |
| -|-----→..
|____|
| |
| .. | vector
|____| _______________ __________
| | | | | | | | |
| -|---------→|pname|object-|--------→|xc|yc|zc|
|____| |_____|_______| |__|__|__|
| |
| -|-----→..
|____|
| |
| .. | rot
|____| _______________ ____________
| | | | | | |
| -|---------→|pname|object-|--------→|xf(array) |
|____| |_____|_______| |__________|
| |
| -|-----→...
|____|
| |
| .. | frame
|____| _______________ ______________________________________
| | | | | | | | | | | | |
| -|---------→|pname|object-|--------→|pname|xf|dad|son|ebro|ybro|howlinked|
|____| |_____|_______| |_____|__|_|_|_|_|_|__|_|__|_________|
| | ↓ ↓ ↓ ↓
| -|-----→.. frame
|____|
| |
| .. | trans
|____| _______________ ____________
| | | | | | |
| -|---------→|pname|object-|--------→|xf(array) |
|____| |_____|_______| |__________|
| |
| -|-----→...
|____|
| |
| .. |
|____|
There are many predeclared symbols. Their pointers and values are described
in the session about initialization.
Some other pointers are defined, to keep track of some important information,
or to define some variable internally used, as
rptr(frame)
F_ARM, used to remember what arm is holding the pointer,
F_FID used to store the rptr to the FIDUCIAL point (when defined)
rptr(trans)
ARRAY T_CSTR[1:3] used to store the frames defined by CONSTRUCT
without arguments
INITIALIZATIONS
PROCEDURES REQUIRING TO BE INITIALIZED
INIBRK
initializes the break tables
initializes the string $BLANK
sets the number of decimal characters for real numbers to 3.
INISYM
initializes the symbol table.
VARIABLES REQUIRING TO BE INITIALIZED
$ALFL←"DECLAR.AL"
default name for input/output file
$EPS = 0.001
is a value used for tests while using trigonometric functions
$READ←FALSE
used by readcode: true while reading
PREDECLARED POINTY VARIABLES
the first element is the pname (printname) of the variable
the second is the record_pointer to the record of class SYMBOL
the third is the record_pointer to the record of the particular
class (SCALAR,VECTOR,ROT,TRANS,FRAME). These names begin with the
initial of the class name and an underscore
the fourth is the value. Values for rot, frame and trans are expressed
by six elements (w,ph,th,x,y,z) corresponding to
(rot(zhat,th)*rot(yhat,ph)*rot(zhat,w),vector(x,y,z))
pname(string) rptr(symbol) rptr(scalar) value
BHAND HANDB S_BHAND
YHAND HANDY S_YHAND
DEG DEG 1
DEGREE DEGREE 1
DEGREES DEGRES 1
INCH INCH 1
INCHES INCHES 1
rptr(vector)
XHAT XHAT V_XHAT (1,0,0)
YHAT YHAT V_YHAT (0,1,0)
ZHAT ZHAT V_ZHAT (0,0,1)
NILVECT NILVECT V_NILVECT (0,0,0)
rptr(rot)
NILROTN NILROTN R_NILROTN (0,0,0) euler angles
rptr(frame)
STATION WORLD F_WRLD (0,0,0,0,0,0)
BPARK BPARK F_BPARK (0,180,0,
43.53125,56.855,9.95875)
YPARK YPARK F_YPARK (0,180,0,40,14,9)
BARM BARM F_BARM
YARM YARM F_YARM (0,0,0,0,0,0)
BGRASP BGRASP F_BGRASP (-180,180,0,0,0,0)
affixed to BARM
POINTER POINTER F_POINTER (-.417,13.2,-5.173,
.0121,.119,3.75)
affixed to BARM
rptr(trans)
NILTRANS NILTRANS T_NILTRANS (0,0,0,0,0,0)
******* important notice
If other predefined variables are inserted the values in the array SAVE of
the procedure RESET in MAINPR.SAI have to be accordingly modified;
FILE TABLE ORGANIZATION
POINTY allows to write on different files, to save or to close them. The
names of the files used, and their status are mantained in a file table,
with an index to know the number of used files. That table is constituted
by two matrices. The information about one file can be found in those two
matrices on the same row.
* $NAMEFL is a string array [1:10], used for the file names
* $CHNFL is an integer array [1:10,0:1], containing in the first
column the status open(0)/close(1) of the file and in the second
column the number of the channel associated.
* $TOTFL is an integer, whose value is the total number of files
used.
$NAMEFL $CHNFL
______________ __________________________
|name of file| |open/close | channel # |
|(string) | | 0 1 | |
|____________| |___________|____________|
| | | | |
| | | | |
The last file used for output is the default name for output instruction.
We'll call this "current file".
* $ALFL, is a string containing the name of the current file
(initialized with DECLAR.AL). When the current file is closed the
name of a previously used open file is taken as default, or, if
there are'nt any DECLAR.AL is taken. The current file is indicated
on the display by a * before the file name.
* $ALCH is the channel number of the current file.
The output of TTY can be saved in a file. The name of this file is given
at the start of the session, and can be changed only by teh instruction
CLOSE_FILES. In the case the TTY output has to be recorded same variables
are used
* $OUT, is used as a flag to know if the output has to be saved
* $TTYFL is the name of the file
* $TTYCH is the channel number.
The input in TTY is converted in upper cases, so lower or upper cases can
be used in any combination. There are problems only while recovering,
because the characters typed into the line editor are not converted.
For READ instruction the global variables used are
* $READ, used as flag to know if teh instruction being executed is
a READ instruction. The flag is required to continue with the
reading in the case the file contains an error, since each error
causes a goto to the main loop
* $INPCH is the number of the channel associated with the input
file. It'used to remember the channel number after an error, and
it's also used by SAVE instruction, when the saved file is read
and copied.
DISPLAY
Some of the procedures for the display are in DISPLY.SAI[1,MLG].
The global variables used in that file are prefixed by ∂.
That file requires
source file load_module
DPYSUB.HDR[SUB,SYS] DPYSUB.SAI[SUB,SYS] DPYSUB[SUB,SYS]
The procedure INIDPY initializes the display, assigning the appropriate
values to the variables defining margin positions.
∂DLMAR ∂SCFR ∂DRMAR
↓ ↓ ↓
_________________________________
∂DTMAR→ | | |
| | |
| FRAME TREE | SC |
| TRANS |-------| ← ∂SCDF
∂TRFL→ |_______________________|_______|
| | | |
| FILE | ROT | VT |
|__________|__________|_________|
∂TPMAR→
↑ ↑
∂FLRT ∂RTVT
∂DBMAR →
The internal/external variables are
$ARROW position of the arrow
external integer
$BLANK string of blanks
external string
$BRCHR break character
external integer
$BSKTAB break table used to get ride of blanks
external integer
$DPYTAB breaktable used to split the string
external integer in different lines)
$EPS
external real
$NCHAR number of characters for frame space
internal integer (=∂wfr/∂chwid)
The external procedure used is
EULERO(REAL ARRAY XF;REFERENCE REAL W,PH,TH);
The global variables of DISPLY.SAI are
∂BUF buffer for the display
integer array [1:1000]
∂CHWID width of a character(=15 on DD,12 on III)
∂CHIGH height of a line (=20)
∂DBMAR bottom margin of the display (=-510 on DD,
-450 on III)
∂DLMAR left margin (=-625 on DD,-510 on III)
∂DRMAR right margin (=580 on DD, 510 on III)
∂DTMAR top margin (=450)
∂DWNLNS number of lines for lower part of display
(=12)
∂FLRT margin between files and rot's
(=-330 on DD,-215 on III)
∂RTVT margin between rot's and vectors
(=175 on DD, 147 on III)
∂SCDF margin between defaults and scalars(=-10)
∂SCFR margin between frames and scalars
(=400 ON DD,330 ON III)
∂SIZE size of the characters (=2)
∂TPMAR typing space top margin (=-318 on DD,
-270 on III)
∂TRFL trans's bottom margin (=-70)
∂UPLNS number of lines in upper part of display
(= 26)
∂WFR width of space for frame tree
(=1015 on DD,830 on III)
∂WRTVT width of space for vectors and rot's
(=495 on DD,352 on III)
∂WSC width of space for scalars (=170)
The procedure ARROW draws an arrow on the screen
. 80 . 20 .
c3y ..................3.....................
. |\ . 10
c12y 1 ________________2| \ .................
| . \ .
c4y | . \4 20
| . /.
|__________________ /................
c67y 7. 6| / . 10
c5y ..................|/....................
. 5 .
. . .
c17x c2356x c4x ;
It's possible to move by one line the array with the instruction ↑ , ↓ than can
be preceded by any integer number.
HOW "KILL" IS IMPLEMENTED
The kill instruction allows to delete the last instruction and its side
effects. Each time an instruction is parsed the kill is initialized by
INIKIL, which sets to null_record the record pointer to the record (or
list of records) used to save the information about the variables being
modified. That record pointer is KILL.
With that implementation would be very easy to save more than one
instruction, allowing so to delete more than the last one. It's enough to
save not only one record pointer (for the last instruction), but more
record pointers (in a circular list)and for each instruction delete the
oldest and insert the new one.
The records used are of class SAVED, where
saved:addr is the address (row number) in $YMTAB of the symbol
modified.
saved:type is the type of that symbol (#sc,#vt,#rt,#fr,#tr) or
is a special type #nw, for new symbols, or #nwfr, for
new frames. That choice ease the handling of the record.
saved:symbol is the record pointer to the record of class symbol.
If the symbol is a new defined one is null_record.
saved:object is the record pointer to the record of one type (#sc,
#vt,#rt,#fr,#tr) if the record is created in the same
instruction (so the type is #nw or #nwfr) or to the record
used to copy the value of the modified variable.
saved:dad is the record pointer to the dad of the frame (used only
when tree modifications are involved).
saved:link is the kind of affixment (same as before).
saved:next is the pointer to next record of the same type. All
the records used to save information about one POINTY
instruction are connected by this link, so it's possible
to follow that chain to restore the previous situation.
How to save the information
There are two main procedures to save the status of a variable
SAVNEW, saves the status of a variable being defined in the instruction
itself. In that case saved:addr is taken from $ENTRY, and saved:object
is the rptr to the symbol. So if the instruction is killed is enough
to delete the symbol from $YMTAB.
SAVOLD, saves the status of a variable previously defined which
is modified in the instruction. A variable is used to remember the
address in $YMTAB of teh last checked symbol, $ROW. Its value is
inserted in saved:addr. Then a new record is constructed
How to recover
To know what was the last instruction a variable $LAST is used. The different
types of instructions are
kil, for not killable instructions
decl, for declarations
del, for deletion
asg, for assignment
afx, for affix/unfix
cpy, for copy and merge
DEFAULT PARTS
POINTY provides many default parts in different instructions.
For the instructions of movement there is a part of the instruction that
can be left out. The system remembers the last movement instruction used
and supply the first part of it as default. That part is displayed on the
little box below the scalar box on the display.
Two global variables are used to store that default part (OLDCMD and OLDOBJ).
default part part of the instruction to be typed in
____________________________________________________________________________________
OLDCMD OLDOBJ
____________________________________________________________________________________
MOVE <frame_id> BY <vector>
TO <frame_id> {+<vector> {WRT <frame_id>}}
MOVEX {<frame_id>} BY <scalar>
MOVEY {<frame_id>} BY <scalar>
MOVEZ {<frame_id>} BY <scalar>
OPEN <hand> TO <scalar>
BY <scalar>
CLOSE <hand> TO <scalar>
BY <scalar>
DRIVE BJT(<number>) TO <scalar>
BY <scalar>
Other instructions allow a default part
instruction default
___________________________________________________________________________________
BARM← INPUT BARM (to update arm position)
CENTER BARM
CLOSE current file
<id>←INPUT POINTER
YARM← INPUT YARM (to update arm position)
OPEN TO|BY <scalar> BHAND
MOVEX BY <scalar> BARM
MOVEY BY <scalar> BARM
MOVEZ BY <scalar> BARM
READ DECLAR.AL
SAVE current file
WRITE current file FROM STATION
WRITE FROM <frame_id> current file
WRITE <file> FROM STATION
vector, rot.... *INCH, *INCHES, *DEG, *DEGREES (no dimension check)
HOW "CONSTRUCT" IS IMPLEMENTED
Three positions are required to define a reference system:
at the origin
on one main axis
on the plane between the previous axis and another main axis
So, given three vectors v1, v2 and v3, and knowing
v1 is at the origin of the frame
v2 is on the axis first_axis (f_axis)
v3 is on the plane through f_axis and second_axis (s_axis)
we want to determine the third_axis (t_axis).
We'll associate an integer number to each one of the main axes, to ease
the computations
xhat 1
yhat 2
zhat 3
Those numbers relates the name of the axis to the number of the column in
the rotation matrix corresponding to it.
* the vector v1 gives the position of the new frame
(column 4 of the rotation matrix);
* the vector |v2-v1| gives the f_axis
(columm f_axis of the rotation matrix);
To determine the other two main axes (column s_axis and t_axis of the
rotation matrix) there are two different cases:
a) b)
↑ f_axis ↑ f_axis
v2 v2
v3 | | v3
| |
v1-----------→ t_axis v1------------→ s_axis
/ /
s_axis t_axis
/ /
t_axis = ||v2-v1|*|v3-v1|| t_axis = ||v3-v1|*|v2-v1||
s_axis = t_axis * f_axis s_axis = f_axis * t_axis
To determine what is the appropriate case it's enough to check if s_axis
follows f_axis in the regular order (case a) or not (case b). This check,
using the numbers associated to the axes, is a simple test on
permutations.
HOW TO HANDLE THE ROTATION MATRIX
POINTY uses a rotation matrix [1:5,1:4],
the part [1:3,1,3] is the rotation matrix
the part [1:3,4] is the translation part
the part [4,1:4] is used only to ease the operations
the part [5,1:3] contains the euler angles corresponding to the rot
the element [5,4] is >0 when the euler angles are valid
!!!!!!! WARNING the rotation matrix [1:3,1:3] used by POINTY is the transpose
of the rotation matrix used by the interface and by AL.
* how to compute the rotation matrix
The procedure XYZROT used to compute the rotation matrix, for a rotation
of an angle W about the axis V (with components CX, CY, CZ about the main
axes ) constructs the matrix with the following formulas
MATRIX ROT(3,3)$
ROT(1,1) := - COS(W)*CX**2 + COS(W) + CX**2$
ROT(1,2) := - COS(W)*CX*CY + CX*CY - CZ*SIN(W)$
ROT(1,3) := - COS(W)*CX*CZ + CX*CZ + CY*SIN(W)$
ROT(2,1) := - COS(W)*CX*CY + CX*CY + CZ*SIN(W)$
ROT(2,2) := - COS(W)*CY**2 + COS(W) + CY**2$
ROT(2,3) := - COS(W)*CY*CZ - CX*SIN(W) + CY*CZ$
ROT(3,1) := - COS(W)*CX*CZ + CX*CZ - CY*SIN(W)$
ROT(3,2) := - COS(W)*CY*CZ + CX*SIN(W) + CY*CZ$
ROT(3,3) := - COS(W)*CZ**2 + COS(W) + CZ**2$
(the previous rows are in a form readable by REDUCE 2)
The procedure SETROT is used to compute directly the rotation matrix
corresponding to ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W) given the Euler
angles W, PH and TH (see next section).
* how to decode the rotation matrix
Usually the matrix is decoded computing the Euler angles.
To show how the decode process is done we'll show the symbolic product of
three rotations
ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W)
(the interpretation of the product between rotations is the same as in AL)
given the three rotation matrices
MATRIX MATZTH(3,3); COMMENT ROT(ZHAT,TH);
MATZTH(1,1) := COS(TH);
MATZTH(1,2) := - SIN(TH);
MATZTH(1,3) := 0;
MATZTH(2,1) := SIN(TH);
MATZTH(2,2) := COS(TH);
MATZTH(2,3) := 0;
MATZTH(3,1) := 0;
MATZTH(3,2) := 0;
MATZTH(3,3) := 1;
MATRIX MATZPH(3,3); COMMENT ROT(YHAT,PH);
MATYPH(1,1) := COS(PH);
MATYPH(1,2) := 0;
MATYPH(1,3) := SIN(PH);
MATYPH(2,1) := 0;
MATYPH(2,2) := 1;
MATYPH(2,3) := 0;
MATYPH(3,1) := - SIN(PH);
MATYPH(3,2) := 0;
MATYPH(3,3) := COS(PH);
MATRIX MATZW(3,3); COMMENT ROT(ZHAT,W);
MATZW(1,1) := COS(W);
MATZW(1,2) := - SIN(W);
MATZW(1,3) := 0;
MATZW(2,1) := SIN(W);
MATZW(2,2) := COS(W);
MATZW(2,3) := 0;
MATZW(3,1) := 0;
MATZW(3,2) := 0;
MATZW(3,3) := 1;
we'll obtain the product
MATRIX EULER(3,3); COMMENT ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W);
EULER(1,1) := COS(W)*COS(TH)*COS(PH) - SIN(W)*SIN(TH);
EULER(1,2) := - (COS(W)*SIN(TH) + SIN(W)*COS(TH)*COS(PH));
EULER(1,3) := COS(TH)*SIN(PH);
EULER(2,1) := COS(W)*SIN(TH)*COS(PH) + SIN(W)*COS(TH);
EULER(2,2) := COS(W)*COS(TH) - SIN(W)*SIN(TH)*COS(PH);
EULER(2,3) := SIN(TH)*SIN(PH);
EULER(3,1) := - COS(W)*SIN(PH);
EULER(3,2) := SIN(W)*SIN(PH);
EULER(3,3) := COS(PH);
The procedure EULERO extract from that matrix the angles W, PH, TH.
Sometimes we need to decode the rotation matrix in a different way. For
example to construct a matrix corresponding to the arm position, but with
the ZHAT pointing up vertically, we need to decode the matrix as a product
of rotations about the three main axes, and then to use only the component
of the rotation about ZHAT.
To show how the decode process is done we'll show the symbolic product of
three rotations
ROT(ZHAT,C)*ROT(YHAT,B)*ROT(ZHAT,A)
(the interpretation of the product between rotations is the same as in AL)
MATRIX MATX(3,3); COMMENT ROT(XHAT,A);
MATX(1,1) := 1;
MATX(1,2) := 0;
MATX(1,3) := 0;
MATX(2,1) := 0;
MATX(2,2) := COS(A);
MATX(2,3) := - SIN(A);
MATX(3,1) := 0;
MATX(3,2) := SIN(A);
MATX(3,3) := COS(A);
MATRIX MATY(3,3); COMMENT ROT(YHAT,B);
MATY(1,1) := COS(B);
MATY(1,2) := 0;
MATY(1,3) := SIN(B);
MATY(2,1) := 0;
MATY(2,2) := 1;
MATY(2,3) := 0;
MATY(3,1) := - SIN(B);
MATY(3,2) := 0;
MATY(3,3) := COS(B);
MATRIX MATZ(3,3); COMMENT ROT(ZHAT,C);
MATZ(1,1) := COS(C);
MATZ(1,2) := - SIN(C);
MATZ(1,3) := 0;
MATZ(2,1) := SIN(C);
MATZ(2,2) := COS(C);
MATZ(2,3) := 0;
MATZ(3,1) := 0;
MATZ(3,2) := 0;
MATZ(3,3) := 1;
we'll obtain the product
MATRIX MATPROD(3,3); COMMENT ROT(ZHAT,C)*ROT(YHAT,B)*ROT(XHAT,A);
MATPROD(1,1) := COS(C)*COS(B);
MATPROD(1,2) := SIN(A)*COS(C)*SIN(B) - SIN(C)*COS(A);
MATPROD(1,3) := SIN(A)*SIN(C) + COS(C)*SIN(B)*COS(A);
MATPROD(2,1) := SIN(C)*COS(B);
MATPROD(2,2) := SIN(A)*SIN(C)*SIN(B) + COS(C)*COS(A);
MATPROD(2,3) := - SIN(A)*COS(C) + SIN(C)*SIN(B)*COS(A);
MATPROD(3,1) := - SIN(B);
MATPROD(3,2) := SIN(A)*COS(B);
MATPROD(3,3) := COS(B)*COS(A);
The procedure DECODE decodes the matrix computing the three angles A, B
and C. When the angle B is 90 degrees it's impossible to compute A and C,
but it's only possible to compute their difference. The procedure DECODE
forces A to 0 and returns in C the value of (C-A).
THE EXPRESSION EVALUATOR
The expression evaluator is a top down single stage evaluator.
It tries to perform computations as soon as something is in a stage
that can be evaluated. No explicit tree of the parsed expression
is built up - an implicit tree is formed by means of the three recursive
routines that evaluate expressions, terms and factors.
The syntax of the three are as follows:
expressions E: <+ T | - T | T > {+T|-T}
term T: F { * F | / F }
factor F: ( E , E, E,... ) or | E |
or func(E,...)
or E WRT E
or E REL E
The expression evaluator (invoked by GTEXPR)
returns a record pointer to a record
of class TREE which have fields DATA and DTYPE.
DATA is a record pointer field of class SCALAR,VECTOR,TRANS,ROT or FRAME
DTYPE is an integer which indicates which record class DATA corresponds to.
The expression evaluator calls the following procedures:
GTOKEN which returns the next symbol and its type
OPSCAL,OPVET,etc which perform the actual arithmetic
ERROR which is defined by POINTY and is the routine for
handling error messages. Since ERROR does not return
control to the expression evaluator, continuation
of computation is started afresh with the corrected
expression.
APRIL 21, 1978.
The expression evaluator has been changed to complete the
process in two passes. At the first pass, the expression tree is
built up. It is then evaluated on a second pass. The implementation
was changed to allow the use of the same expression evaluator to build
up the internal form of a function expression. Functions can now been evaluated.
The problem of dummy variables and global variables that
are deleted have been addressed. The dummy parameter of a function
must be an undeclared identifier, but those names can be used later on.
When the function definition is edited, there will be no confusion between
the old parameters and new variables of the same name.
The use of global variables in function definitions presents
the problem of ensuring that the function is no longer valid when
the global variable is deleted. This is done by maintaining pointers
in the symbol record that keep track of which functions/variables
make use /are used by which.
Thus when a global variable is deleted, POINTY will inform the user
of its effect, and prevent evaluation of functions using that
variable, until the user redefines that variable again.
[msm]
SOURCE FILE PROTOCOL
To keep track of the different files and the procedures in them,
a new protocol has been set up. Each file to be compiled into a load
module must have an id associated with it, and must call an auxilliary
header file HEADER.SAI[PNT,HE] of internal/external declarations.
HEADER.SAI[PNT,HE] has the following properties:
It has a version number associated with it that it gives to
each of the files that are compiled using it. The version number should
be changed each time any changes are made in HEADER.SAI that affect
more than one of the other files.
HEADER.SAI is responsible for all macro definitions, abbreviations,
and conditional compilation falgs not otherwise defined. It declares
all variables either internal or external depending on the flag
associated with it. Procedures are declared forward internal or
external so that SAIL can check for errors made in the type or
nature of the procedures and the arguments associated with it.
Internal declarations only of procedures can exist in source files.
ALL other internal and external declarations MUST be made in the
HEADER.SAI.
HEADER.SAI is also responsible for calling all the other
source files and loadmodules necessary for a particular compiled
module. This is to ensure that all source_file and load_module
requirements are kept together in one place, and hopefully to make it
easier to trace the absence of missing files during loadng and to
tell where the reference to it is located.
At the current time, HEADER.SAI does not do the whole
thing to completion yet. There are still a few places where
reference is made to load_modules and source_files within the
source_file being compiled, but this should go away soon.